package impl

import (
	"encoding/hex"
	"time"

	"hundsun.com/hsl/hschain/common/log/logging"
	"hundsun.com/hsl/hschain/common/types"
	"hundsun.com/hsl/hschain/common/util/pb"
	"hundsun.com/hsl/hschain/protos"
	exe "hundsun.com/hsl/hschain/protos/execute"
	"hundsun.com/hsl/hschain/protos/ledger"
)

func (chain *BlockChain) addHistroyTxs(num uint64, txHash protos.Hash) {
	txHashStr := hex.EncodeToString(txHash)
	chain.historyTxs.hash2BlockNum[txHashStr] = num
	hashs := chain.historyTxs.blockNum2Hashs[num]
	hashs = append(hashs, txHashStr)
	chain.historyTxs.blockNum2Hashs[num] = hashs
}

func (chain *BlockChain) delExpireHistroyTxs(num uint64) {
	// del history
	if num > uint64(chain.txRecordNum) {
		minHistoryNum := num - uint64(chain.txRecordNum)
		if hashs, ok := chain.historyTxs.blockNum2Hashs[minHistoryNum]; ok {
			for _, hash := range hashs {
				delete(chain.historyTxs.hash2BlockNum, hash)
			}
			delete(chain.historyTxs.blockNum2Hashs, minHistoryNum)
		}
	}
}

func (chain *BlockChain) isExistTxHistory(txHash protos.Hash) bool {
	txHashStr := hex.EncodeToString(txHash)
	if _, ok := chain.historyTxs.hash2BlockNum[txHashStr]; ok {
		return true
	}
	return false
}

// OnBlockAccepted 接收到blockchain提供的新区块
func (chain *BlockChain) OnBlockAccepted(curBlkHeight uint64) {
	chain.blocksAcceptMutex.Lock()
	defer chain.blocksAcceptMutex.Unlock()

	chainLog := chain.log
	chainLog.Infof("Accept new block number %d", curBlkHeight)

	if curBlkHeight == 0 || //创世块在startup处理
		curBlkHeight <= uint64(chain.curRecvBlockNum) { //区块已经处理过了
		return
	}

	start := uint64(chain.curRecvBlockNum) + 1
	end := curBlkHeight
	chainLog.Debugf("no continuous block, need get blocks start %d end %d ", start, end)

	//每次取5个
	step := uint64(5)
	for i := start; i <= end; i += step {
		mid := i + step - 1
		if mid > end {
			mid = end
		}
		blocks, err := chain.GetBlocks(i, mid)
		if err != nil {
			return
		}
		//将区块放入通道
		for _, block := range blocks {
			if block != nil {
				chain.blocksCh <- block
				chain.curRecvBlockNum++
			}
		}
	}
}

// checkBlockExecResult 检查block的执行结果，如果不匹配这说明是重复交易需要填充重复交易
func (chain *BlockChain) checkBlockExecResult(block *ledger.Block, execRsts *ledger.BlockExecResult) {
	txCount := len(block.Transactions)
	blockNum := block.GetHeader().GetNumber()
	execRstsLen := len(execRsts.GetTransactionExecResults())
	if execRstsLen == txCount {
		return
	}
	if execRstsLen < txCount {
		txRsts := make([]*ledger.TransactionExecResult, txCount)
		j := 0
		for i := 0; i < txCount; i++ {
			if j < execRstsLen && execRsts.TransactionExecResults[j].GetReceipt().GetTransactionIndex() == int32(i) {
				txRsts[i] = execRsts.TransactionExecResults[j]
				j++
			} else {
				txRsts[i] = &ledger.TransactionExecResult{
					Receipt: &ledger.TransactionReceipt{
						BlockNumber:      blockNum,
						TransactionIndex: int32(i),
						TxStatus:         ledger.TransactionReceipt_DUPLICATED_TX,
						TxHash:           block.Transactions[i].Hash()},
				}
			}
		}
		execRsts.TransactionExecResults = txRsts
	}
}

// ProcessGenesisBlock process genesis block
func (chain *BlockChain) ProcessGenesisBlock() {
	genesisBlock := pb.GetGenesisBlock()

	locationTxs := make([]*LocationTx, len(genesisBlock.Transactions))
	for index, tx := range genesisBlock.Transactions {
		locationTxs[index] = &LocationTx{index: index, tx: tx}
	}
	txGroups := group(locationTxs)
	//放入交易分组通道
	blockTxGroups := &types.BlockTxGroups{genesisBlock, txGroups}
	chain.txGroupsCh <- blockTxGroups
	//设置链区块为0
	chain.curBlockNum = 0
	chain.curRecvBlockNum = 0
	//var blkEntry = &ecom.BlockEntry{}
	//blkEntry.Block = genesisBlock
	//chain.blockCache.Set(ecom.GenTempBlockEntryKeyByHeight(0), blkEntry)
	chain.blockStore.SaveGenesisBlock(genesisBlock)
}

// ProcessNewBlocks process new blocks
func (chain *BlockChain) ProcessNewBlocks() {
	log := chain.log
	for {
		select {
		case <-chain.ctx.Done():
			chain.log.Infof("ProcessNewBlocks exit...")
			return
		case validatedBlock := <-chain.blocksCh:
			//从通道读取区块
			mqLog := log.WithField(logging.LogFieldBlockNumber, validatedBlock.Header.Number)
			mqLog.Debugf("Start process new bloc,begin dedup tx count %d ", len(validatedBlock.Transactions))
			if validatedBlock == nil || validatedBlock.Header == nil {
				continue
			}

			blockNum := validatedBlock.Header.Number
			chain.curBlockNum = int64(blockNum)
			start := time.Now()
			var locationTxs []*LocationTx
			for index, tx := range validatedBlock.Transactions {
				txHash := tx.Hash()
				if !chain.isExistTxHistory(txHash) { //交易去重
					locationTxs = append(locationTxs, &LocationTx{index: index, tx: tx})
					chain.addHistroyTxs(blockNum, txHash)
				}
			}
			mqLog.WithField(logging.LogFieldCost, time.Since(start)/time.Millisecond).Infof("end dedup transactions tx count %d ", len(validatedBlock.Transactions))
			chain.delExpireHistroyTxs(blockNum)

			start = time.Now()
			var txGroups []*exe.TransactionGroup // 没有需要执行的交易，可能交易全部是重复的
			if len(locationTxs) != 0 {
				//分组交易
				txGroups = group(locationTxs)
			}
			mqLog.WithField(logging.LogFieldCost, time.Since(start)/time.Millisecond).Infof("generate group num is %d, tx num is %d", len(txGroups), len(validatedBlock.Transactions))
			//放入交易分组通道
			blockTxGroups := &types.BlockTxGroups{validatedBlock, txGroups}
			chain.txGroupsCh <- blockTxGroups
		}
	}
}
